package cn.wss.flowable.service.impl;

import cn.wss.flowable.common.Page;
import cn.wss.flowable.common.Params;
import cn.wss.flowable.model.FlowableProcessInstance;
import cn.wss.flowable.model.FlowableTask;
import cn.wss.flowable.service.Conditions;
import cn.wss.flowable.service.FlowableHistoryService;
import cn.wss.flowable.service.FlowableTaskService;
import com.greenpineyu.fel.FelEngine;
import com.greenpineyu.fel.FelEngineImpl;
import com.greenpineyu.fel.context.FelContext;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ChangeActivityStateBuilder;
import org.flowable.engine.runtime.Execution;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.*;

public class FlowableTaskServiceImpl implements FlowableTaskService, Conditions {

    @Autowired
    private TaskService taskService;

    @Autowired
    private FlowableHistoryService flowableHistoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private RepositoryService repositoryService;

    @Override
    public Page<FlowableTask> pageTask(Map<String, Object> variables) {
        Page<FlowableTask> page = new Page(variables);
        Params params = new Params(variables);
        TaskQuery query = getTaskQuery();

        if (params.containsKey(ID)) {
            query.taskId(params.get(ID));
        }
        if (params.containsKey(ASSIGNEE)) {
            query.taskAssignee(params.get(ASSIGNEE));
        }
        if (params.containsKey(USER)) {
            query.taskCandidateUser(params.get(USER));
        }
        if (params.containsKey(GROUP)) {
            query.taskCandidateGroup(params.get(GROUP));
        }
        if (params.containsKey(PROCESS_INSTANCE_ID)) {
            query.processInstanceId(params.get(PROCESS_INSTANCE_ID));
        }
        if (params.containsKey(PROCESS_DEFINITION_ID)) {
            query.processDefinitionId(params.get(PROCESS_DEFINITION_ID));
        }
        if (params.containsKey(PROCESS_DEFINITION_KEY)) {
            query.processDefinitionKey(params.get(PROCESS_DEFINITION_KEY));
        }
        if (params.containsKey(BUSINESS_KEY)) {
            query.processInstanceBusinessKey(params.get(BUSINESS_KEY));
        }
        // 节点是否过期
        if (params.containsKey(IS_DUE)) {
            // true: 已过期  false：未过期
            boolean dueFlag = params.getBoolean(IS_DUE);
            if (dueFlag) {
                query.taskDueBefore(new Date());
            } else {
                query.taskDueAfter(new Date());
            }
        }
        // 添加排序类型
        if (params.containsKey(SORT)) {
            if ("asc".equals(params.get(SORT))) {
                query = query.orderByTaskCreateTime().asc();
            } else {
                query = query.orderByTaskCreateTime().desc();
            }
        }

        // 流程变量查询
        if (params.containsKey("variables")) {
            List<Map<String, Object>> list = params.getList("variables");
            if (list != null && !list.isEmpty()) {
                for (Map<String, Object> data: list) {
                    String type = (String)data.get("variableType");
                    if (StringUtils.isBlank(type)) {
                        continue;
                    }
                    switch (type) {
                        case "equals":
                            query.processVariableValueEquals((String)data.get("variableName"), data.get("variableValue"));
                            break;
                        case "notEquals":
                            query.processVariableValueNotEquals((String)data.get("variableName"), data.get("variableValue"));
                            break;
                        case "greaterThan":
                            query.processVariableValueGreaterThan((String)data.get("variableName"), data.get("variableValue"));
                            break;
                        case "greaterThanOrEqual":
                            query.processVariableValueGreaterThanOrEqual((String)data.get("variableName"), data.get("variableValue"));
                            break;
                        case "lessThan":
                            query.processVariableValueLessThan((String)data.get("variableName"), data.get("variableValue"));
                            break;
                        case "lessThanOrEqual":
                            query.processVariableValueLessThanOrEqual((String)data.get("variableName"), data.get("variableValue"));
                            break;
                        default:
                            query.processVariableValueLike((String)data.get("variableName"), "%" + data.get("variableValue") + "%");
                            break;
                    }
                }
            }
        }

        int totalElements = query.list().size();
        List<Task> data = query.listPage((page.getPageNum() - 1) * page.getPageSize(), page.getPageSize());
        List<FlowableTask> tasks = new ArrayList<>();
        for (Task item: data) {
            FlowableTask task = new FlowableTask();
            BeanUtils.copyProperties(item, task);

            FlowableProcessInstance processInstance = flowableHistoryService.instance(item.getProcessInstanceId());
            task.setProcessDefinitionKey(processInstance.getProcessDefinitionKey());
            task.setProcessDefinitionName(processInstance.getProcessDefinitionName());
            task.setComments(taskService.getTaskComments(task.getId()));

            task.setVariable(flowableHistoryService.getVariables(item.getProcessInstanceId()));
            tasks.add(task);
        }
        page.setData(tasks);
        page.setTotalElements(totalElements);
        return page;
    }

    @Override
    public void complete(String id, Map<String, Object> variables, Map<String, Object> localVariables) {
        complete(id, variables, localVariables, null);
    }

    @Override
    public void complete(String id, Map<String, Object> variables, Map<String, Object> localVariables, String comment) {
        if (StringUtils.isBlank(id)) {
            throw new RuntimeException("id不能为空");
        }
        Task task = getTaskQuery().taskId(id).singleResult();
        if (task == null) {
            throw new RuntimeException("未查询到相关任务");
        }
        if (localVariables != null && localVariables.keySet().size() > 0) {
            taskService.setVariablesLocal(id, localVariables);
        }
        if (StringUtils.isNotBlank(comment)) {
            taskService.addComment(id, task.getProcessInstanceId(), comment);
        }
        if (variables != null && variables.keySet().size() > 0) {
            taskService.complete(id, variables);
        } else {
            taskService.complete(id);
        }
    }

    @Override
    public void setAssignee(String id, String assignee) {
        taskService.setAssignee(id, assignee);
    }

    @Override
    public void callbackPrevious(String taskId, Map<String, Object> data) {
        Task task = getTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new RuntimeException("未查询到相关任务");
        }
        ExecutionEntity ee = (ExecutionEntity) runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
        String currentActivityId = ee.getActivityId();

        //流程图信息
        BpmnModel bpmnModel = repositoryService.getBpmnModel(ee.getProcessDefinitionId());
        Process mainProcess = bpmnModel.getMainProcess();
        Collection<FlowElement> flowElements = mainProcess.getFlowElements();

        //当前节点信息
        FlowNode flowNode = (FlowNode)bpmnModel.getFlowElement(currentActivityId);
        SequenceFlow sequenceFlow = flowNode.getIncomingFlows().get(0);
        String sourceRef = sequenceFlow.getSourceRef();
        FlowElement nextFlowElement = getFlowElementById(sourceRef, flowElements);
        ChangeActivityStateBuilder builder = runtimeService.createChangeActivityStateBuilder().processVariables(data)
                .processInstanceId(task.getProcessInstanceId());
        if (nextFlowElement instanceof ParallelGateway) {
            // 并行网关需要将活动的节点都结束掉
            sourceRef = getSourceRef(bpmnModel, flowElements, sequenceFlow.getSourceRef());
            List<String> executionIds = new ArrayList<>();
            List<Execution> executions = runtimeService.createExecutionQuery().parentId(task.getProcessInstanceId()).list();
            executions.forEach(s -> executionIds.add(s.getId()));
            builder.moveExecutionsToSingleActivityId(executionIds, sourceRef).changeState();
        } else if (nextFlowElement instanceof ExclusiveGateway) {
            sourceRef = getSourceRef(bpmnModel, flowElements, sequenceFlow.getSourceRef());
            builder.moveActivityIdTo(currentActivityId, sourceRef).changeState();
        } else {
            builder.moveActivityIdTo(currentActivityId, sourceRef).changeState();
        }
    }

    @Override
    public Map<String, Object> getParams(String taskId) {
        Map<String, Object> variables = taskService.getVariables(taskId);
        if (variables == null) {
            return new HashMap<>();
        }
        if (variables.containsKey("taskListener")) {
            variables.remove("taskListener");
        }
        if (variables.containsKey("executionListener")) {
            variables.remove("executionListener");
        }
        return variables;
    }

    @Override
    public Object getParamsByVariableName(String taskId, String variableName) {
        return taskService.getVariable(taskId, variableName);
    }

    @Override
    public Map<String, Object> getLocalParams(String taskId) {
        return taskService.getVariablesLocal(taskId);
    }

    @Override
    public Object getLocalParamsByVariableName(String taskId, String variableName) {
        return taskService.getVariableLocal(taskId, variableName);
    }

    @Override
    public void setParams(String taskId, String variableName, Object variableValue) {
        taskService.setVariable(taskId, variableName, variableValue);
    }

    @Override
    public void setParams(String taskId, Map<String, Object> variables) {
        taskService.setVariables(taskId, variables);
    }

    @Override
    public void setLocalParams(String taskId, String variableName, Object variableValue) {
        taskService.setVariableLocal(taskId, variableName, variableValue);
    }

    @Override
    public void setLocalParams(String taskId, Map<String, Object> variables) {
        taskService.setVariablesLocal(taskId, variables);
    }

    /**
     * 当前节点是否是最后一个用户任务
     * @param taskId
     * @param map
     * @return
     */
    @Override
    public boolean isLastUserTask(String taskId, Map<String, Object> map) {
        return getNextUserTasks(taskId, map).size() == 0;
    }

    /**
     * 获取当前节点之后的用户任务
     * @param taskId
     * @param map
     * @return
     */
    public List<UserTask> getNextUserTasks(String taskId,Map<String,Object> map) {
        List<UserTask> data = new ArrayList<>();
        Task task =taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new RuntimeException("未查询到正在处理的任务，请传递当前正在处理的任务节点");
        }
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        Process mainProcess = bpmnModel.getMainProcess();
        Collection<FlowElement> flowElements = mainProcess.getFlowElements();
        String key = task.getTaskDefinitionKey();
        FlowElement flowElement = bpmnModel.getFlowElement(key);
        next(flowElements, flowElement, map, data, flowElement.getId());
        return data;
    }

    /**
     * 获取下一节点
     * @param flowElements 定义的节点集合
     * @param flowElement 当前任务节点
     * @param map 变量集合用来存储排他网关走是的分支的变量值
     * @param userTasks
     * @param currentId
     */
    private void next(Collection<FlowElement> flowElements, FlowElement flowElement,Map<String,Object> map,List<UserTask> userTasks,String currentId) {
        //如果是结束节点
        if (flowElement instanceof EndEvent) {

        }
        //获取Task的出线信息--可以拥有多个
        List<SequenceFlow> outGoingFlows = new ArrayList<>();
        if (flowElement instanceof org.flowable.bpmn.model.Task) {
            outGoingFlows = ((org.flowable.bpmn.model.Task) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof Gateway) {
            outGoingFlows = ((Gateway) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof StartEvent) {
            outGoingFlows = ((StartEvent) flowElement).getOutgoingFlows();
        }
        //遍历所有的出线--找到可以正确执行的那一条
        for (SequenceFlow sequenceFlow : outGoingFlows) {
            //1.有表达式，且为true
            //2.无表达式
            String expression = sequenceFlow.getConditionExpression();
            if (expression == null ||
                    Boolean.valueOf(
                            String.valueOf(
                                    result(map, expression.substring(expression.lastIndexOf("{") + 1, expression.lastIndexOf("}")))))) {
                //出线的下一节点
                String nextFlowElementID = sequenceFlow.getTargetRef();
                //查询下一节点的信息
                FlowElement nextFlowElement = getFlowElementById(nextFlowElementID, flowElements);
                //用户任务
                if (nextFlowElement instanceof UserTask) {
                    boolean flg = false;
                    UserTask currentUserTask = null;
                    for (UserTask userTask : userTasks) {
                        if (currentId.equals(userTask.getId())) {
                            currentUserTask = userTask;
                        }
                        if (userTask.getId().equals(nextFlowElementID)) {
                            flg = true;
                        }
                    }
                    if (flg) {
                        userTasks.remove(currentUserTask);
                        break;
                    }
                    userTasks.add((UserTask) nextFlowElement);
                    next(flowElements, nextFlowElement, map, userTasks,currentId);
                }
                //排他网关
                else if (nextFlowElement instanceof ExclusiveGateway) {
                    next(flowElements, nextFlowElement, map, userTasks,currentId);
                }
                //并行网关
                else if (nextFlowElement instanceof ParallelGateway) {
                    next(flowElements, nextFlowElement, map, userTasks,currentId);
                }
                //接收任务
                else if (nextFlowElement instanceof ReceiveTask) {
                    next(flowElements, nextFlowElement, map, userTasks,currentId);
                }
                //服务任务
                else if (nextFlowElement instanceof ServiceTask) {
                    next(flowElements, nextFlowElement, map, userTasks,currentId);
                }
                //子任务的起点
                else if (nextFlowElement instanceof StartEvent) {
                    next(flowElements, nextFlowElement, map, userTasks,currentId);
                }
                //结束节点
                else if (nextFlowElement instanceof EndEvent) {
                }
            }
        }
    }

    private  Object result(Map<String, Object> map, String expression) {
        FelEngine fel = new FelEngineImpl();
        FelContext ctx = fel.getContext();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            ctx.set(entry.getKey(), entry.getValue());
        }
        Object result = fel.eval(expression);
        return result;
    }

    // 打回上一步时获取上一步节点id
    String getSourceRef(BpmnModel bpmnModel, Collection<FlowElement> flowElements, String currentActivityId) {
        FlowNode flowNode = (FlowNode)bpmnModel.getFlowElement(currentActivityId);
        SequenceFlow sequenceFlow = flowNode.getIncomingFlows().get(0);
        String sourceRef = sequenceFlow.getSourceRef();
        FlowElement nextFlowElement = getFlowElementById(sourceRef, flowElements);
        // 网关类型继续往上找
        if (nextFlowElement instanceof ExclusiveGateway || nextFlowElement instanceof ParallelGateway) {
            return getSourceRef(bpmnModel, flowElements, sourceRef);
        }
        return sourceRef;
    }

    private FlowElement getFlowElementById(String Id, Collection<FlowElement> flowElements) {
        for (FlowElement flowElement : flowElements) {
            if (flowElement.getId().equals(Id)) {
                return flowElement;
            }
        }
        return null;
    }

    public TaskQuery getTaskQuery() {
        return taskService.createTaskQuery();
    }
}
